package com.talkweb.player.components;

import com.talkweb.playerlib.ResourceTable;
import com.talkweb.securitycamera.common.base.BaseFraction;
import com.talkweb.securitycamera.common.constans.ScreenChangeType;
import com.talkweb.securitycamera.common.interfaces.play.IPlay;
import com.talkweb.securitycamera.common.interfaces.play.IPlayCallBack;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.Image;
import ohos.agp.components.ProgressBar;
import ohos.agp.components.Text;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.window.service.WindowManager;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.global.resource.RawFileDescriptor;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.common.Source;
import ohos.media.player.Player;

/**
 * @version 1.0.1
 * @description: Fraction for play video
 * @program: security-camera
 * @Author chenbenqing
 * @Date 2021/1/29 17:23
 */
public class PlayerFraction extends BaseFraction implements IPlay<Source>, Component.ClickedListener {
    private static final HiLogLabel logLabel = new HiLogLabel(HiLog.LOG_APP, 0x001010, "PlayerFraction");
    private static final int EVENT_CODE_PROGRESS = 1;
    private static final int EVENT_CODE_COMPLETE = 2;

    private static final int INTERVAL_UPDATE_PROGRESS = 500;

    private DependentLayout mRootLayout;
    private Image mImgBack;
    private Image mImgCamera;
    private Image mImgPlay;
    private Image mImgFullScreen;
    private ProgressBar mProgressBar;
    private Text mTxtSpeed;
    private Text mTxtVideoSrc;
    private SurfaceProvider mSurfaceProvider;

    private Player mPlayer;
    private IPlayCallBack mPlayCallback;
    private Source mVideoSource;
    private boolean isFullScreen = false;
    private int mOriginHeight;
    private int mRootLayoutWidth;
    private int mRootLayoutHeight;

    private EventHandler mEventHandler = new EventHandler(EventRunner.getMainEventRunner()) {
        @Override
        protected void processEvent(InnerEvent event) {
            switch (event.eventId) {
                case EVENT_CODE_PROGRESS:
                    if (mPlayer != null && mPlayer.getDuration() > 0) {
                        int progress = (int) ((float) mPlayer.getCurrentTime() * 100 / mPlayer.getDuration());
                        mProgressBar.setProgressValue(progress);

                        if (mPlayer.isNowPlaying()) {
                            mEventHandler.sendEvent(EVENT_CODE_PROGRESS, INTERVAL_UPDATE_PROGRESS);
                        }
                    }
                    break;
                case EVENT_CODE_COMPLETE:
                    mImgPlay.setPixelMap(ResourceTable.Media_ic_play);
                    mProgressBar.setProgressValue(100);
                    break;
                default:
                    break;
            }
            super.processEvent(event);
        }
    };

    @Override
    public void play(Source source) {
        if (source == null) {
            return;
        }
        mVideoSource = source;
        mPlayer.setSource(mVideoSource);
        mPlayer.prepare();
        adjustSurfaceSize();
        playVideo();
    }

    private void playVideo() {
        if (mSurfaceProvider.getSurfaceOps().isPresent()) {
            mPlayer.setVideoSurface(mSurfaceProvider.getSurfaceOps().get().getSurface());
        }
        mPlayer.play();

        mEventHandler.sendEvent(EVENT_CODE_PROGRESS, INTERVAL_UPDATE_PROGRESS);
    }

    @Override
    public Component getPlayComponent() {
        return this.getComponent();
    }

    @Override
    public int getLayoutId() {
        return ResourceTable.Layout_fraction_player;
    }

    @Override
    public void initializeComponent(Component component) {
        mRootLayout = findComponent(ResourceTable.Id_player_root);
        mImgBack = findComponent(ResourceTable.Id_image_back);
        mImgCamera = findComponent(ResourceTable.Id_image_camera);
        mImgPlay = findComponent(ResourceTable.Id_image_play);
        mProgressBar = findComponent(ResourceTable.Id_video_progress);
        mImgFullScreen = findComponent(ResourceTable.Id_image_full_screen);
        mTxtSpeed = findComponent(ResourceTable.Id_txt_speed);
        mTxtVideoSrc = findComponent(ResourceTable.Id_video_src);

        mImgBack.setClickedListener(this);
        mImgPlay.setClickedListener(this);
        mImgFullScreen.setClickedListener(this);

        // 解决surfaceProvider 不设置pinToZTop(true)，视频播放不了的问题:
        // 1.pinToTop设置false，保证其他控件与surfaceProvider在同一layout下，并且不能设置背景
        //2.增加以下代码设置顶部窗口透明 WindowManager.getInstance().getTopWindow().get().setTransparent(true);
        // 详情：https://developer.huawei.com/consumer/cn/forum/topic/0203466344105310153?fid=0101303901040230869
        mSurfaceProvider = findComponent(ResourceTable.Id_surface_provider);
        if (mSurfaceProvider.getSurfaceOps().isPresent()) {
            mSurfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallback());
        }
        mSurfaceProvider.pinToZTop(false);
        WindowManager.getInstance().getTopWindow().get().setTransparent(true);

        mPlayer = new Player(getFractionAbility());
        mPlayer.setPlayerCallback(new VideoPlayerCallback());

        mOriginHeight = component.getHeight();

        mRootLayout.setLayoutRefreshedListener(component1 -> {
            mRootLayoutWidth = component1.getWidth();
            mRootLayoutHeight = component1.getHeight();
            HiLog.info(logLabel, "onRefreshed w/h: " + mRootLayoutWidth + "/" + mRootLayoutHeight);
            adjustSurfaceSize();
        });
    }

    @Override
    protected void onComponentDetach() {
        HiLog.info(logLabel, "onComponentDetach ");
        if (mPlayer != null) {
            mPlayer = null;
        }
        if (mEventHandler != null) {
            mEventHandler.removeAllEvent();
        }
        super.onComponentDetach();
    }

    @Override
    public void onClick(Component component) {
        if (component == mImgBack) {
            if (mPlayCallback != null) {
                if (isFullScreen) {
                    isFullScreen = !isFullScreen;
                    mImgFullScreen.setPixelMap(ResourceTable.Media_ic_screen_expand);
                    mPlayCallback.onScreenChange(ScreenChangeType.SCREEN_CHANGE_EXIT_FULL);
                } else {
                    mPlayCallback.exitPlayCallBack();
                }
            }
        } else if (component == mImgPlay) {
            if (mPlayer.isNowPlaying()) {
                mPlayer.pause();
            } else {
                playVideo();
            }
            mImgPlay.setImageAndDecodeBounds(mPlayer.isNowPlaying() ? ResourceTable.Media_ic_stop : ResourceTable.Media_ic_play);
        } else if (component == mImgFullScreen) {
            isFullScreen = !isFullScreen;
            mImgFullScreen.setPixelMap(isFullScreen ? ResourceTable.Media_ic_screen_collapse : ResourceTable.Media_ic_screen_expand);
            if (mPlayCallback != null) {
                mPlayCallback.onScreenChange(isFullScreen ? ScreenChangeType.SCREEN_CHANGE_ENTER_FULL : ScreenChangeType.SCREEN_CHANGE_EXIT_FULL);
            }
        }
    }

    private void prepareLocalSource() {
        try {
            RawFileDescriptor filDescriptor = getFractionAbility().getResourceManager().getRawFileEntry("resources/rawfile/test2.mp4").openRawFileDescriptor();
            mVideoSource = new Source(filDescriptor.getFileDescriptor(), filDescriptor.getStartPosition(), filDescriptor.getFileSize());
            mPlayer.setSource(mVideoSource);
            mPlayer.prepare();
            adjustSurfaceSize();
            play(mVideoSource);
        } catch (Exception e) {
            HiLog.error(logLabel, "prepareLocalSource Exception:" + e.getMessage());
        }
    }

    public IPlayCallBack getPlayCallback() {
        return mPlayCallback;
    }

    public void setPlayCallback(IPlayCallBack mPlayCallback) {
        this.mPlayCallback = mPlayCallback;
    }

    public int getOriginHeight() {
        return mOriginHeight;
    }

    // 根据视频宽高比，计算调整 surface 的宽高
    private void adjustSurfaceSize() {
        if (mPlayer == null || mPlayer.getVideoWidth() == -1 || mSurfaceProvider == null) {
            return;
        }

        float videoRate = mPlayer.getVideoWidth() / (float) mPlayer.getVideoHeight();
        int rootW = mRootLayoutWidth;
        int rootH = mRootLayoutHeight;
        if (rootW > rootH * videoRate) {
            rootW = (int) (rootH * videoRate);
        } else {
            rootH = (int) (rootW / videoRate);
        }
        mSurfaceProvider.setWidth(rootW);
        mSurfaceProvider.setHeight(rootH);
    }

    private class SurfaceCallback implements SurfaceOps.Callback {

        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
            HiLog.info(logLabel, "surfaceCreated");
            if (surfaceOps != null) {
                prepareLocalSource();
            }
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
            HiLog.info(logLabel, "surfaceChanged: " + i + "," + i1 + "," + i2);
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
            HiLog.info(logLabel, "surfaceDestroyed");
        }
    }

    private class VideoPlayerCallback implements Player.IPlayerCallback {

        @Override
        public void onPrepared() {
            HiLog.info(logLabel, "onPrepared");
        }

        @Override
        public void onMessage(int i, int i1) {
            HiLog.info(logLabel, "onMessage: i= " + i + ", i1= " + i1);
        }

        @Override
        public void onError(int i, int i1) {
            HiLog.info(logLabel, "onError: i=" + i + ", i1=" + i1);
        }

        @Override
        public void onResolutionChanged(int i, int i1) {
            HiLog.info(logLabel, "onResolutionChanged: i= " + i + ", i1= " + i1);
        }

        @Override
        public void onPlayBackComplete() {
            HiLog.info(logLabel, "onPlayBackComplete");
            if (mPlayer != null) {
                mPlayer.stop();
            }
            if (mPlayCallback != null) {
                mPlayCallback.exitPlayCallBack();
            }
            mEventHandler.removeAllEvent();
            mEventHandler.sendEvent(EVENT_CODE_COMPLETE);
        }

        @Override
        public void onRewindToComplete() {
            HiLog.info(logLabel, "onRewindToComplete");
        }

        @Override
        public void onBufferingChange(int i) {
            HiLog.info(logLabel, "onBufferingChange: i= " + i);
        }

        @Override
        public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
            HiLog.info(logLabel, "onNewTimedMetaData: timestampUs= " + mediaTimedMetaData.timestampUs);
        }

        @Override
        public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
            HiLog.info(logLabel, "onMediaTimeIncontinuity: mediaTimeUs= " + mediaTimeInfo.mediaTimeUs);
        }
    }
}
